package ru.susu.algebra.partition.polyhedron.checkers;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import ru.susu.algebra.methods.simplex.SimplexMethod;
import ru.susu.algebra.methods.simplex.exceptions.SimplexException;
import ru.susu.algebra.operation.IOperation;
import ru.susu.algebra.partition.Partition;
import ru.susu.algebra.partition.PartitionsFactory;
import ru.susu.algebra.partition.polyhedron.VertexHelper;
import ru.susu.algebra.partition.vectex.Partition2VertexCache;
import ru.susu.algebra.wrapper.number.BigIntegerFraction;

/**
 * @author akargapolov
 * @since: 11.03.2010
 */
public class SimplexMethodVertexChecker implements IVertexChecker
{
	private IOperation<Boolean> _secondIncludesOp = new SecondVertexIncludesCheckerOp();
	public static boolean DEBUG = false;

	@Override
	public String check(Partition partition, List<Partition> allPartitions)
	{
		List<Integer[]> partitions = convertPartitions(partition, allPartitions);

		if (partitions.size() == 1)
			return CHECKED_RESULT;

		Integer[][] matrix = createSimplexMatrix(partitions);
		Integer[] function = createFunction(partitions);

		List<BigIntegerFraction> result;

		try
		{
			result = new SimplexMethod().run(matrix, function);
		}
		catch(SimplexException t)
		{
			return CHECKED_RESULT;
		}
		if (!checkGoodSolution(result))
		{
			return CHECKED_RESULT;
		}

		String solution = printSolution(partitions, result);
		if (DEBUG)
		{
			System.out.print(solution);
		}
		return solution;
	}

	private List<Integer[]> convertPartitions(Partition partition, List<Partition> allPartitions)
	{
		List<Integer[]> result = new ArrayList<Integer[]>();
		Integer[] first = Partition2VertexCache.getVertex(partition);
		result.add(first);
		for (Partition p : allPartitions)
			if (!partition.equals(p))
			{
				Integer[] vertex = Partition2VertexCache.getVertex(p);
				if (_secondIncludesOp.performOperation(first, vertex))
					result.add(vertex);
			}
		return result;
	}

	private Integer[][] createSimplexMatrix(List<Integer[]> partitions)
	{
		Integer[] lineNumbers = getLineNumbers(partitions.get(0));
		int n = partitions.size() - 1;
		int m = lineNumbers.length + 1;
		Integer[][] matrix = new Integer[m][n+1];

		for (int i = 0;i<m - 1;i++)
			for (int j = 0;j< partitions.size();j++)
				matrix[i][j] = partitions.get(j)[lineNumbers[i]];

		//x1+x2+x3+.....+xn = 1
		for (int j = 0;j< partitions.size();j++)
			matrix[m - 1][j] = 1;

		return matrix;
	}

	private Integer[] getLineNumbers(Integer[] partition)
	{
		List<Integer> result = new ArrayList<Integer>();
		for (int index = 0;index < partition.length;index++)
			if (partition[index] != 0)
				result.add(index);
		return result.toArray(new Integer[result.size()]);
	}

	private Integer[] createFunction(List<Integer[]> partition)
	{
		Integer[] function = new Integer[partition.size() - 1];
		Arrays.fill(function, 1);
		return function;
	}

	private String printSolution(List<Integer[]> partitions, List<BigIntegerFraction> result)
	{
		StringBuffer buffer = new StringBuffer();
		printArray(partitions.get(0), buffer);
		int number = 0;
		for (int i = 0;i<result.size();i++)
		{
			if (BigIntegerFraction.ZERO.equals(result.get(i)))
			{
				continue;
			}
			if (number == 0) buffer.append(" = "); else buffer.append(" + ");
			buffer.append(result.get(i) + " *");
			printArray(partitions.get(i), buffer);
			number++;
		}
		return "[" + number + "] " + buffer.toString();
	}

	private boolean checkGoodSolution(Collection<BigIntegerFraction> solution)
	{
		BigIntegerFraction sum = BigIntegerFraction.ZERO;
		for (BigIntegerFraction current : solution)
		{
			if (current.compareTo(BigIntegerFraction.ZERO) < 0)
				return false;
			sum = sum.add(current);
		}
		return sum.equals(BigIntegerFraction.ONE);
	}

	private void printArray(Integer[] array, StringBuffer buffer)
	{
		List<Integer> part = new ArrayList<Integer>();
		for (int index = 0; index < array.length; index++)
		{
			for (int j = 0; j < array[index]; j++)
				part.add(index + 1);
		}
		Integer[] array2 = part.toArray(new Integer[part.size()]);
		buffer.append(VertexHelper.getVertexString(PartitionsFactory.createPartition(array2)));
	}

}
